﻿using Fishing.Abstractions;
using Fishing.Common;
using Fishing.Controls;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Input;

namespace Fishing.View
{
    public partial class WndProcMsgWindow : Window
    {
        private readonly ISettingsService _settingsService;
        private readonly IKeyBoardHookService _keyBoardHookService;
        private readonly IProgramController _programController;

        private KeyGesture _quickUpdateKeyBinding;
        private KeyGesture _forceQuitKeyBinding;
        private KeyGesture _exhaleKeyBinding;

        private int _hookId;
        Win32.KeyboardHookProc KeyboardHookDelegate;

        public WndProcMsgWindow()
        {
            InitializeComponent();

            WindowStartupLocation = WindowStartupLocation.Manual;
            Left = -99999;

            _settingsService = App.Services.GetRequiredService<ISettingsService>();
            _keyBoardHookService = App.Services.GetRequiredService<IKeyBoardHookService>();
            _programController = App.Services.GetRequiredService<IProgramController>();

            _quickUpdateKeyBinding = _settingsService.Settings.QuickUpdateKeyBinding;
            _forceQuitKeyBinding = _settingsService.Settings.ForceQuitKeyBinding;
            _exhaleKeyBinding = _settingsService.Settings.ExhaleKeyBinding;

            _settingsService.Settings.KeyBindingChanged += Settings_KeyBindingChanged;
        }

        private void Settings_KeyBindingChanged(object? sender, EventArgs e)
        {
            _quickUpdateKeyBinding = _settingsService.Settings.QuickUpdateKeyBinding;
            _forceQuitKeyBinding = _settingsService.Settings.ForceQuitKeyBinding;
            _exhaleKeyBinding = _settingsService.Settings.ExhaleKeyBinding;
        }

        private void Window_SourceInitialized(object sender, EventArgs e)
        {
            var hInstance = Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]);

            KeyboardHookDelegate = new Win32.KeyboardHookProc(KeyboardHookProc);

            _hookId = Win32.SetWindowsHookEx(13, KeyboardHookDelegate, hInstance, 0);
        }

        private int KeyboardHookProc(int nCode, int wParam, IntPtr lParam)
        {
            if (nCode < 0)
            {
                return Win32.CallNextHookEx(_hookId, nCode, wParam, lParam);
            }

            if (wParam == 0x101)
            {
                return Win32.CallNextHookEx(_hookId, nCode, wParam, lParam);
            }

            if (Marshal.PtrToStructure(lParam, typeof(Win32.KBDLLHOOKSTRUCT)) is not Win32.KBDLLHOOKSTRUCT keyData)
            {
                return Win32.CallNextHookEx(_hookId, nCode, wParam, lParam);
            }

            if (_keyBoardHookService.IsOnlyMainHook())
            {
                if (KeyInterop.KeyFromVirtualKey(keyData.vkCode) == _exhaleKeyBinding.Key && Keyboard.Modifiers == _exhaleKeyBinding.Modifiers)
                {
                    _programController.Toggle();
                }

                if (KeyInterop.KeyFromVirtualKey(keyData.vkCode) == _quickUpdateKeyBinding.Key && Keyboard.Modifiers == _quickUpdateKeyBinding.Modifiers)
                {
                    App.Services.GetRequiredService<MainWindow>().QuickUpdateCommand.Execute(null);
                }

                return Win32.CallNextHookEx(_hookId, nCode, wParam, lParam);
            }

            CheckUpdaterInputBindings(keyData);

            if (CheckSystemComboKey(keyData))
            {
                return 1;
            }

            return keyData.vkCode switch
            {
                0x5b or 0x5c or 0x1b => 1,
                _ => Win32.CallNextHookEx(_hookId, nCode, wParam, lParam),
            };
        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            Win32.UnhookWindowsHookEx(_hookId);
        }

        private void CheckUpdaterInputBindings(Win32.KBDLLHOOKSTRUCT keyData)
        {
            var key = KeyInterop.KeyFromVirtualKey(keyData.vkCode);

            if (key == Key.Space)
            {
                ((Updater)App.Services.GetRequiredService<MainWindow>().GetCurrentFishingView().Content).GetViewModel().AddPercentCommand.Execute(null);

                return;
            }

            if (key == _forceQuitKeyBinding.Key && Keyboard.Modifiers == _forceQuitKeyBinding.Modifiers)
            {
                App.Services.GetRequiredService<MainWindow>().GetCurrentFishingView().ForceQuitCommand.Execute(null);

                return;
            }
        }

        private static bool CheckSystemComboKey(Win32.KBDLLHOOKSTRUCT keyData)
        {
            var key = KeyInterop.KeyFromVirtualKey(keyData.vkCode);

            if ((key == Key.Tab && Keyboard.Modifiers == ModifierKeys.Alt) ||
                (key == Key.F4 && Keyboard.Modifiers == ModifierKeys.Alt))
            {
                return true;
            }

            return false;
        }
    }
}